package com.extra_archive.framework.net 
{
	import com.extra_archive.framework.net.events.LoaderEvent;

	import flash.display.LoaderInfo;
	import flash.events.*;
	import flash.utils.Timer;

	//==================================================================
	//@class name  :  LoaderSequencer.as
	//@author          : vkimone. KimKiJeung  (http://kimkijeung.com)
	//@last update   : 2007. 09. 11
	//@version         : V1.1
	//==================================================================
	
	/**
	 * 순차적으로 로드를 진행할 수 있는 시퀀스 로더 (image,swf).    
	 * 
	 * <p>기본적으로 로더에 등록되는 이벤트 리스너는 
	 *  <code>LoaderSequencer</code>의 load method를 사용하여 원하는 갯수의 파일만큼 추가할 수 있다. 
	 * 
	 * @usage  
	 *  <listing> 
	 *    
	 *     import com.dstrict.ub.utils.loader.*;
			import com.dstrict.ub.events.*;
			
	        var loader:LoaderSequencer = new LoaderSequencer();
			loader.addEventListener(LoaderEvent.START, onLoadStart);
			loader.addEventListener(LoaderEvent.ALL_LOADED, onAllLoadFinished);
			loader.addEventListener(LoaderEvent.COMPLETE, onLoadDone);
			loader.addEventListener(LoaderEvent.PROGRESS, onLoadProgress);
			loader.addEventListener(LoaderEvent.ERROR, onIOError);

			
			loader.add("image1.jpg","1");
			loader.add("image2.jpg","2");
			loader.add("image3.jpg","3");
			loader.add("image4.jpg","4");
			loader.execute();
			
			function onLoadStart(evt:LoaderEvent):void{
				trace(evt.toString()+"-------->","start");
			}
			
			function onLoadDone(evt:LoaderEvent):void {
				//trace(evt.fileName);
				this["container"+evt.fileName].addChild(evt.loader.content);
				
			}
			function onAllLoadFinished(evt:LoaderEvent):void {
				trace(evt.toString()+"-------->","onAllLoadFinished");
			}
			function onLoadProgress(evt:LoaderEvent):void {
				trace(evt.toString()+"-------->","progress"+"--->"+evt.bytesLoaded +" : "+evt.bytesTotal);
			}

			function onIOError(evt:LoaderEvent):void{
				trace(evt.toString()+"-------->",evt.message)
			}
	 *  
	 *  </listing>
	 */
	
	public class LoaderSequencer extends EventDispatcher {
		private var _workerCount : uint = 1;
		private var _fileQueue : Array;
		private var _workers : Array;
		private var _isloading : Boolean;
		private var _timer : Timer;
		
		/**
		 * Constructor function
		 *  
		 * @param inThreadCount (optional)   동시에 로드할수 있는 파일갯수 . Default 값은  1. 
		 *                                                         <br> 즉 동시에 설정한 갯수만큼 파일을 로드할수 있다는 의미.
		 *                                                         <br> 값이 1일경우 한개씩 순차적으로 로드한다.
		 */
		public function LoaderSequencer(inThreadCount : uint = 1) {
			setWorkerCount(inThreadCount);
			_isloading = false;
		}
		
		//==================================================================
		////////////////////////////////    PROPERTY    /////////////////////////////////////////////////////////////////////
		//==================================================================
		/**
		 * 로딩할 파일갯수
		 */
		public function get fileCount() : Number {
			return _fileQueue.length;
		}
		
		//==================================================================
		////////////////////////////////    PUBLIC METHOD    //////////////////////////////////////////////////////////////
		//==================================================================
		/**
		 * @param fileUrl      로드할 컨테츠 url
		 * @param fileInfo    로드할 파일 식별자 이름( unique index 역할), 
		 *                             <code>LoaderEvent</code> 객체의 fileInfo 를 통해 참조가능
		 */
		public function add(fileUrl : String,fileInfo : String) : Boolean {
			var fileData : FileData = new FileData(fileUrl, fileInfo);
			_fileQueue.push(fileData);
			
			
			// load 메서드를 통해 외부에서 로드할 파일을 여러개 설정할경우 모든 로드정보를 
			//  세팅할 시간적 여유 주기위해 25ms 후 로딩이 시작된다.
			  
//			_timer = new Timer(25, 1);
//			_timer.addEventListener(TimerEvent.TIMER, onTimer);
//			_timer.start();

			return true;
		}
		
		public function execute():void{
			getNextInQueue();	
		}
		
		public function stopAllLoading() : void {
			var i : Number, len : Number = _workers.length;
			for (i = 0;i < len; ++i) {
				var w : LoaderWorker = LoaderWorker(_workers[i]);
				if (w.loading == true) {
					w.stopLoading();
				}
			}
			_fileQueue = new Array();
		}
		
		override public function toString() : String {
			return "com.dstrict.framework.utils.loader.LoaderSequencer";
		}
		
		//==================================================================
		////////////////////////////////    EVENT LISTENER    //////////////////////////////////////////////////////////////
		//==================================================================
		final private function onTimer(evt : TimerEvent) : void {
			getNextInQueue();
			_timer.removeEventListener(TimerEvent.TIMER, onTimer);
		}
		
		
		 final private function onWorkerLoadStart(evt:Event):void{
		 	var target : LoaderWorker = LoaderWorker(evt.target);
		 	dispatchEvent(new LoaderEvent(LoaderEvent.START, target.loader,target.fileData.name));
		 }
		 
		 /**
		 *   <code>LoaderWorker</code>  오브젝트의 로딩진행 과정 모니터링  <p> LoaderSequencer 의 리스너로 이벤트 전달
		 */
		final private function onWorkerLoadProgress(evt : ProgressEvent) : void {
			var target : LoaderWorker = LoaderWorker(evt.target);
			var totalAndLoaded : Object = getTotalAndLoaded();
			
			var e : LoaderEvent=new LoaderEvent(LoaderEvent.PROGRESS, target.loader,target.fileData.name);
			e.bytesLoaded=totalAndLoaded.loaded;
			e.bytesTotal=totalAndLoaded.total;
			dispatchEvent(e);
		}
		
		/**
		 * <code>LoaderWorker</code> 오브젝트의 로딩완료시 
		 * LoaderSequencer 의 리스너로 이벤트 전달
		 * 
		 */
		final private function onWorkerLoadDone(evt : Event) : void {
			var target : LoaderWorker = LoaderWorker(evt.target);
			dispatchEvent(new LoaderEvent(LoaderEvent.COMPLETE,target.loader,target.fileData.name));
			
			var isStillLoading : Boolean = false;
			var i : int, len : int = _workers.length;
			for (i = 0;i < len; ++i) {
				var w : LoaderWorker = LoaderWorker(_workers[i]);
		
				if (w.loading) {
					isStillLoading = true;
				}
			}
            
			getNextInQueue(evt);
		}
		
		final private function onWorkerLoadError(evt : IOErrorEvent) : void {
			var target : LoaderWorker = LoaderWorker(evt.target);
			var e : LoaderEvent=new LoaderEvent(LoaderEvent.ERROR, target.loader,target.fileData.name);
			e.message=evt.text;
			dispatchEvent(e);
		}
		
		//==================================================================
		////////////////////////////////    PRIVATE METHOD    /////////////////////////////////////////////////////////////
		//==================================================================
		/**
		 *   loadingworker 설정
		 *   @param  inThreadCount      동시로딩갯수.
		 */
		final private function setWorkerCount(inThreadCount : uint) : void {
			if (inThreadCount > 0) {
				_workerCount = inThreadCount;
			}
			_fileQueue = new Array();
			_workers = new Array();
			for (var i : int = 0;i < _workerCount; ++i) {
				var worker : LoaderWorker = new LoaderWorker();
				worker.addEventListener(Event.OPEN, onWorkerLoadStart);
				worker.addEventListener(ProgressEvent.PROGRESS, onWorkerLoadProgress);
				worker.addEventListener(Event.INIT, onWorkerLoadDone);	
				worker.addEventListener(IOErrorEvent.IO_ERROR, onWorkerLoadError);	
				_workers.push(worker);
			}
		}
		
		final private function removeListener() : void {
			for (var i : int = 0;i < _workerCount; ++i) {
				var worker : LoaderWorker = new LoaderWorker();			
				worker.removeEventListener(ProgressEvent.PROGRESS, onWorkerLoadProgress);
				worker.removeEventListener(Event.INIT, onWorkerLoadDone);	
				worker.removeEventListener(IOErrorEvent.IO_ERROR, onWorkerLoadError);	
				worker.reset();
				worker = null;
			}
			_workers = null;
		}
		
		final private function getNextInQueue(evt : Event = null) : void {
			if (_fileQueue.length == 0) {
				assertIsLoading();
				if (!_isloading) {
					var target : LoaderWorker = LoaderWorker(evt.target);
					dispatchEvent(new LoaderEvent(LoaderEvent.ALL_LOADED, target.loader,target.fileData.name));
					reset(); //remove listeners of LoaderWorker event 
				}
				return;
			}
			var i : int, len : int = _workers.length;
			for (i = 0;i < len; ++i) {
				var w : LoaderWorker = LoaderWorker(_workers[i]);
				if (w.loading == false) {
					var fileData : FileData = FileData(_fileQueue.shift());
					w.load(fileData);
					_isloading = true;				
					break;
				}
			}
		}
		
		/**
		 * 모든 worker 로부터 total bytes 와 loaded bytes 를 받아 계산
		 * @return A value object {total:uint, loaded:uint}
		 */
		final private function getTotalAndLoaded() : Object {
			var totalAndLoaded : Object = {total:0, loaded:0};
			var i : int, len : int = _workers.length;
			for (i = 0;i < len; ++i) {
				var w : LoaderWorker = LoaderWorker(_workers[i]);
				if (w.loading) {
					var data : LoaderInfo = w.loaderInfo;
					var dataTotal : uint = data.bytesTotal;
					if (dataTotal != 0) totalAndLoaded.total += dataTotal;
					var dataLoaded : uint = data.bytesLoaded;
					if (dataLoaded != 0) totalAndLoaded.loaded += dataLoaded; 
				}
			}
			return totalAndLoaded;
		}
		
		/**
		 * 로딩상태 업데이트, workers 가 로딩중일경우 _isloading 값 true .
		 * <p>모든 loader worker 가 free 할경우  false 값 
		 */
		final private function assertIsLoading() : void {
			var isStillLoading : Boolean = false;
			var i : int, len : int = _workers.length;
			for (i = 0;i < len; ++i) {
				var w : LoaderWorker = LoaderWorker(_workers[i]);
				if (w.loading == true) {
					isStillLoading = true;
				}
			}
			_isloading = isStillLoading;
		}
		
		final private function reset() : void {
			removeListener();
			_fileQueue = null;
		}
	}
}
